home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Utilities / Programming / EnterAct 3.5 / Drag_on Modules / hAWK programs / $ClipFormatFuncIntro < prev    next >
Encoding:
Text File  |  1995-08-13  |  22.2 KB  |  899 lines  |  [TEXT/KEEN]

  1. #$ClipFormatFuncIntro: pretty up the start of a function, down to last local
  2. #variable at beginning of function definition.
  3. # Use the "Set Variables" button in the hAWK dialog to set the value
  4. # of spaces_in_tab if it is different from 4.
  5. # The preset variable "convert_comments", if left to its default value of 1,
  6. # will signal conversion of /*...*/ comments to one or more // comments.
  7. # Set it to 0 if you don't want this.
  8. # Usage: start this program (first adjust the preset vars described above
  9. # if necessary); to reformat a function start, type "ff" just at the start
  10. # of the function, select full lines from there down to the end of the
  11. # local variable declarations at the top of the function, Copy, wait
  12. # for the menu bar to flash, and Paste. If nothing changes, hit <Command><period>
  13. #to stop the program in the usual way, and the stderr window will appear with
  14. # a (rough) diagnostic message of what went wrong. Most common problem, including
  15. # something that isn't a local variable declaration in the local section, such
  16. # as a define or a prototype. (If you want some fun, teach this program to handle them)
  17.  
  18. # This program, like other "magic clipboard" programs, should eventuall be halted
  19. # by typing <Command><period>. Note it does nothing at all unless you Copy something
  20. # that begins with "ff".
  21.  
  22. # Just for example, below you would type in the "ff" just before "Boolean"
  23. # to signal this program that you want a function formatted; then select
  24. # all complete lines from the "ff" down to "shiftDown);", then Copy,...
  25.  
  26. #ffBoolean AddSimilarFiles(Boolean isSysHeader, long *fileKeyPtr) /* start of messy function */
  27. #    {
  28. #    WindowDataPtr    wdPtr = GetProjectPtr(); // hey it's a standard window data pointer
  29. #    char            fileName[32];    /*comment broken across
  30. #                                    two lines */
  31. #    long            startDirID;/* directory to start search from */
  32. #    short                j, index, vRefNum, whichPane;
  33. #    short            q=2;//completely arbitrary comment here
  34. #    Boolean            filesAdded = FALSE;
  35. #    Boolean            searchSubs = (gEvtDetails.optionDown || gEvtDetails.shiftDown);
  36.  
  37. # ...wait for the menu bar to flash, and then Paste to get
  38.  
  39. #Boolean AddSimilarFiles
  40. #    (Boolean    isSysHeader,    // 
  41. #    long        *fileKeyPtr)    // start of messy function 
  42. #    {
  43. #    WindowDataPtr        wdPtr = GetProjectPtr();// hey it's a standard window data pointer
  44. #    char                fileName[ 32 ];        // comment broken across
  45. #                                    // two lines 
  46. #    long                startDirID;            // directory to start search from 
  47. #    short                j;                    // 
  48. #    short                index;                // 
  49. #    short                vRefNum;            // 
  50. #    short                whichPane;            // 
  51. #    short                q =2;                // completely arbitrary comment here
  52. #    Boolean                filesAdded = FALSE;    // 
  53. #    Boolean                searchSubs = (gEvtDetails.optionDown || gEvtDetails.shiftDown);// 
  54.  
  55. # This still has some minor problems -- eg the "start of messy function" comment will probably
  56. # have to be moved up to just above the start of the function - but it was a pretty lame
  57. # comment anyway, and shouldn't really have been there. The multi-line comment that covers
  58. # two lines has been converted to two single-line comments, but the tabbing will have to
  59. # be adjusted. The huge last line might look better if split across two lines.
  60.  
  61.  
  62. # This program runs until terminated with <Command><period>
  63. # It reformats copied function starts nicely, provided the clip begins with "ff"
  64. BEGIN {
  65.     clipCharsToWatch = 32;
  66.     init();
  67.     # Check user-set variables
  68.     if (spaces_in_tab+0 < 1)
  69.         spaces_in_tab = 4;
  70.     if (convert_comments+0 != 0)
  71.         convert_comments = 1;
  72.     while (1) # run until <Command><period>...
  73.         {
  74.         # see if clipboard has changed
  75.         if ((newClip = getclip(clipCharsToWatch)) != oldClip)
  76.             {
  77.             oldClip = newClip;
  78.             # "ff" at start is used as the trigger
  79.             if (index(newClip, "ff") == 1)
  80.                 ReformatFuncIntro();
  81.             }
  82.         }
  83.     }
  84.  
  85. function ReformatFuncIntro()
  86.     {
  87.     # clear toktype, error flag, output
  88.     toktype = 0;
  89.     error = 0;
  90.     out = ""
  91.     var_number = 0;
  92.     last_param = 0;
  93.     last_local = 0;
  94.     # also clean out arrays
  95.     clean_arrays();
  96.     temp = getclip(); # gets calling editor's private clip
  97.     # strip leading ff
  98.     clip = substr(temp, 3);
  99.     temp = "";
  100.     # parse the clip as a function beginning
  101.     func_beginning();
  102.     # output the reformatted function beginning to "out"
  103.     if (error == 0)
  104.         output_reformatted_beginning();
  105.     # send the result back to the calling editor's clip
  106.     if (error == 0)
  107.         putclip(out);
  108.     # update "oldClip"
  109.     if (error == 0)
  110.         oldClip = substr(out, 1, 32);
  111.     # flash menu bar to signal something happened
  112.     if (error == 0)
  113.         beep(0);
  114.     else
  115.         {
  116.         beep(0);
  117.         beep(0);
  118.         beep(0);
  119.         }
  120.     }
  121.  
  122. # clean out var_type[i] var_name[i] var_init[i] var_comment[i]
  123. function clean_arrays()
  124.     {
  125.     for (i in var_type)
  126.         delete var_type[i];
  127.     for (i in var_name)
  128.         delete var_name[i];
  129.     for (i in var_init)
  130.         delete var_init[i];
  131.     for (i in var_comment)
  132.         delete var_comment[i];    
  133.     }
  134.  
  135. # parse the clip as a function beginning
  136. # func_beginning    :    stuff_before_paren '(' param_decls ')' '{' local_decls
  137. function func_beginning()
  138.     {
  139.     # collect stuff_before_paren
  140.     stuff_before_paren();
  141.     # require hit '(' here
  142.     advance();
  143.     require(tok == "(", "No opening paren");
  144.     if (error) return;
  145.     advance();
  146.     # collect param declarations
  147.     param_decls();
  148.     # require ')'
  149.     require(tok == ")", "No closing paren");
  150.     if (error) return;
  151.     advance();
  152.     # pick up any comment after ')'
  153.     if (toktype == COMMENT)
  154.         {
  155.         var_comment[var_number] = tok;
  156.         advance();
  157.         }
  158.     if (error) return;
  159.     # record divider between params and locals
  160.     last_param = var_number;
  161.     # if '{' follows, do the locals too
  162.     if (tok == "{")
  163.         {
  164.         advance();
  165.         # collect local declarations
  166.         local_decls();
  167.         last_local = var_number;
  168.         }
  169.     else if (toktype != EOF)
  170.         require(0, "expected { or nothing after parameters");
  171.     }
  172.  
  173. # match up to just before '(', wherever it is
  174. function stuff_before_paren()
  175.     {
  176.     if (match(clip, /\(/))
  177.         {
  178.         opening = substr(clip, 1, RSTART-1)
  179.         clip = substr(clip, RSTART)
  180.         }
  181.     }
  182.  
  183. # param_decls        :    param_decl {{comment}','{comment} paramdecl}
  184. function param_decls()
  185.     {
  186.     
  187.     while (tok != ")" && toktype != EOF)
  188.         {
  189.         param_decl();
  190.         if (toktype == COMMENT)
  191.             {
  192.             # record the comment
  193.             var_comment[var_number] = tok;
  194.             advance();
  195.             if (tok == ",")
  196.                 {
  197.                 advance();
  198.                 if (toktype == COMMENT)
  199.                     {
  200.                     # record the comment
  201.                     var_comment[var_number] = tok;
  202.                     advance();
  203.                     }
  204.                 }
  205.             }
  206.         else if (tok != "," && tok != ")" && toktype != EOF)
  207.             {
  208.             require(0, "illegal looking params");
  209.             }
  210.         if (tok == ",")
  211.             {
  212.             advance();
  213.             if (toktype == COMMENT)
  214.                 {
  215.                 # record the comment
  216.                 var_comment[var_number] = tok;
  217.                 advance();
  218.                 }
  219.             }
  220.         }
  221.     require(toktype != EOF, "unexpected EOF in function parameters");
  222.     }
  223.  
  224. # param_decl    :    type_name [star_name]
  225. # type_name        :    name | '...'
  226. function param_decl()
  227.     {
  228.     if (toktype == NAME)
  229.         {
  230.         # collect various keywords that might come before a type name proper
  231.         if (tok == "static" || tok == "const" || tok == "volatile"\
  232.         || tok == "extern" || tok == "register"\
  233.         || tok == "signed" || tok == "unsigned")
  234.             {
  235.             var_type[++var_number] = tok;
  236.             advance();
  237.             while (tok == "static" || tok == "const" || tok == "volatile"\
  238.                 || tok == "extern" || tok == "register"\
  239.                 || tok == "signed" || tok == "unsigned")
  240.                 {
  241.                 var_type[var_number] = var_type[var_number] " " tok;
  242.                 advance();
  243.                 }
  244.             #the type name proper
  245.             if (toktype == NAME)
  246.                 {
  247.                 var_type[var_number] = var_type[var_number] " " tok;
  248.                 advance();
  249.                 }
  250.             else
  251.                 require(0, "stat unexpected token while looking for param type");
  252.             }
  253.         else
  254.             {
  255.             var_type[++var_number] = tok;
  256.             advance();
  257.             }
  258.         # the parameter name (with adornments), optional
  259.         if (toktype == "*" || toktype == NAME)
  260.             star_name();
  261.         }
  262.     else
  263.         require(0, "unexpected token while looking for param type");
  264.     }
  265.  
  266. # star_name        :    {'*'}name{'['.*']'}
  267. function star_name()
  268.     {
  269.     if (tok == "*")
  270.         {
  271.         while (tok == "*")
  272.             {
  273.             var_name[var_number] = var_name[var_number] tok;
  274.             advance();
  275.             }
  276.         }
  277.     if (toktype == NAME)
  278.         {
  279.         var_name[var_number] = var_name[var_number] tok;
  280.         advance();
  281.         }
  282.     else
  283.         require(0, "unexpected token while looking for name");
  284.     while (tok == "[")
  285.         {
  286.         var_name[var_number] = var_name[var_number] tok;
  287.         advance();
  288.         while (tok != "]" && toktype != EOF)
  289.             {
  290.             if (tok == ",")
  291.                 var_name[var_number] = var_name[var_number] tok;
  292.             else
  293.                 var_name[var_number] = var_name[var_number] " " tok;
  294.             advance();
  295.             }
  296.         if (toktype != EOF)
  297.             {
  298.             var_name[var_number] = var_name[var_number] " " tok;
  299.             advance();
  300.             }
  301.         else
  302.             require(0, "unexpected end of clip while looking for ]");
  303.         }
  304.     }
  305.  
  306. # collect local declarations
  307. # local_decls        :    local_decl* to end of clip
  308. function local_decls()
  309.     {
  310.     while (toktype != EOF)
  311.         local_decl();
  312.     }
  313.  
  314. # local_decl        :    type_name local_name {',' {comment} local_name} ';' {comment}
  315. # note here var_number is incremented when local_name seen, not when type_name seen.
  316. function local_decl()
  317.     {
  318.     
  319.     if (toktype == NAME)
  320.         {
  321.         if (tok == "static" || tok == "const" || tok == "volatile"\
  322.         || tok == "extern" || tok == "register"\
  323.         || tok == "signed" || tok == "unsigned")
  324.             {
  325.             cur_var_type = tok;
  326.             advance();
  327.             while (tok == "static" || tok == "const" || tok == "volatile"\
  328.                 || tok == "extern" || tok == "register"\
  329.                 || tok == "signed" || tok == "unsigned")
  330.                 {
  331.                 cur_var_type = cur_var_type " " tok;
  332.                 advance();
  333.                 }
  334.             if (toktype == NAME)
  335.                 {
  336.                 cur_var_type = cur_var_type " " tok;
  337.                 advance();
  338.                 }
  339.             else
  340.                 require(0, "stat unexpected token while looking for local var type");
  341.             }
  342.         else
  343.             {
  344.             cur_var_type = tok;
  345.             advance();
  346.             }
  347.         if (toktype == "*" || toktype == NAME)
  348.             {
  349.             local_name();
  350.             # continue for all decls with same type, and pick up comments
  351.             while (tok != ";" && toktype != EOF)
  352.                 {
  353.                 if (tok == ",")
  354.                     advance();
  355.                 if (toktype == COMMENT)
  356.                     {
  357.                     # record the comment
  358.                     var_comment[var_number] = tok;
  359.                     advance();
  360.                     }
  361.                 if (toktype == "*" || toktype == NAME)
  362.                     {
  363.                     local_name();
  364.                     }
  365.                 # may lock up on eg a function declaration
  366.                 if (tok != ";" && tok != ",")
  367.                     require(0, "unexpected token while looking for local name or semicolon or EOF");
  368.                 }
  369.             if (tok == ";")
  370.                 advance();
  371.             if (toktype == COMMENT)
  372.                 {
  373.                 # record the comment
  374.                 var_comment[var_number] = tok;
  375.                 advance();
  376.                 }
  377.             }
  378.         else
  379.             require(0, "unexpected token while looking for local name");
  380.         }
  381.     else
  382.         require(0, "unexpected token while looking for local var type");
  383.     }
  384.  
  385. # local_name        :    star_name {'=' var_init}
  386. function local_name()
  387.     {
  388.     var_type[++var_number] = cur_var_type;
  389.     star_name();
  390.     if (tok == "=")
  391.         {
  392.         var_init[var_number] = " " tok;
  393.         #advance(); - get init now being done behind lexer's back via "match"
  394.         get_initter();
  395.         }
  396.     }
  397.  
  398. # var_init            :    stuff in '{}' or all up to ',' or ';' or EOF
  399. # -left unparsed, to avoid having to put whitespace back in
  400. function get_initter()
  401.     {
  402.     if (tok == "{")
  403.         {
  404.         if (match(clip, /^[^}]+}/))
  405.             {
  406.             var_init[var_number] = var_init[var_number] substr(clip, 1, RLENGTH);
  407.             clip = substr(clip, RLENGTH+1)
  408.             advance();
  409.             }
  410.         else
  411.             require(0, "unbalanced curly initializer");
  412.         }
  413.     else
  414.         {
  415.         if (match(clip, /^[^,;]+[,;]/))
  416.             {
  417.             var_init[var_number] = var_init[var_number] substr(clip, 1, RLENGTH-1);
  418.             clip = substr(clip, RLENGTH)
  419.             advance();
  420.             }
  421.         else
  422.             require(0, "unbalanced initializer");
  423.         }
  424. # Old version, putting whitespace back in proved too tedious....    
  425. #    previous_toktype = toktype;
  426. #    
  427. #    if (tok == "{")
  428. #        {
  429. #        while (tok != "}" && tok != ";" && toktype != EOF)
  430. #            {
  431. #            if (tok == "," || (tok == "(" && previous_toktype == NAME))
  432. #                var_init[var_number] = var_init[var_number] tok;
  433. #            else
  434. #                var_init[var_number] = var_init[var_number] " " tok;
  435. #            previous_toktype = toktype;
  436. #            advance();
  437. #            }
  438. #        if (tok == "}")
  439. #            {
  440. #            var_init[var_number] = var_init[var_number] " " tok;
  441. #            advance();
  442. #            }
  443. #        }
  444. #    while (tok != "," && tok != ";" && toktype != EOF)
  445. #        {
  446. #        if (tok == "(" && previous_toktype == NAME)
  447. #            var_init[var_number] = var_init[var_number] tok;
  448. #        else
  449. #            var_init[var_number] = var_init[var_number] " " tok;
  450. #        previous_toktype = toktype;
  451. #        advance();
  452. #        }
  453.     }
  454.  
  455.  
  456.  
  457. ## output functions **
  458. # Do the output to "out", which will become the new clip.
  459. function output_reformatted_beginning()
  460.     {
  461.     OutputDecl();
  462.     OutputLocals();
  463.     }
  464.  
  465. # Output func intro and nicely formatted params, and '{'.
  466. function OutputDecl(    longest_type, longest_name, first_inc)
  467.     {
  468.     # special handling for case of single param
  469.     if (last_param == 1)
  470.         {
  471.         if (var_name[1] != "")
  472.             var_type[1] = var_type[1] " ";
  473.         if (var_comment[1] != "")
  474.             var_comment[1] = "\t" var_comment[1];
  475.         else
  476.             var_comment[1] = "\t// "
  477.         out = opening "(" var_type[1] var_name[1] ")" var_comment[1] "\r\t{\r";
  478.         }
  479.     else if (last_param > 0)
  480.         {
  481.         out = opening "\r";
  482.         #calculate longest type and param name, and stick the commas in
  483.         longest_type = 0;
  484.         longest_name = 0;
  485.         for (i = 1; i <= last_param; ++i)
  486.             {
  487.             if (i == 1)
  488.                 var_type[i] = "(" var_type[i];
  489.             if (longest_type < length(var_type[i]))
  490.                 longest_type = length(var_type[i])
  491.             if (i < last_param)
  492.                 var_name[i] = var_name[i] ","
  493.             else
  494.                 var_name[i] = var_name[i] ")"
  495.             if (longest_name < length(var_name[i]))
  496.                 longest_name = length(var_name[i])
  497.             }
  498.         # adjust up to next tab boundary
  499.         if (longest_type % spaces_in_tab)
  500.             longest_type += spaces_in_tab - longest_type % spaces_in_tab;
  501.         if (longest_name % spaces_in_tab)
  502.             longest_name += spaces_in_tab - longest_name % spaces_in_tab;
  503.         # and add a tab
  504.         longest_type += spaces_in_tab;
  505.         longest_name += spaces_in_tab;
  506.         # now spit them out
  507.         for (i = 1; i <= last_param; ++i)
  508.             {
  509.             # adjust type names
  510.             diff = longest_type - length(var_type[i])
  511.             first_inc = spaces_in_tab - length(var_type[i]) % spaces_in_tab;
  512.             var_type[i] = "\t" var_type[i];
  513.             if (first_inc == spaces_in_tab)
  514.                 first_inc = 0;
  515.             if (first_inc)
  516.                 {
  517.                 var_type[i] = var_type[i] "\t";
  518.                 diff -= first_inc;
  519.                 }
  520.             while (diff > 0)
  521.                 {
  522.                 var_type[i] = var_type[i] "\t";
  523.                 diff -= spaces_in_tab;
  524.                 }
  525.             # adjust param names (if present)
  526.             if (var_name[i] != "")
  527.                 {
  528.                 diff = longest_name - length(var_name[i])
  529.                 first_inc = spaces_in_tab - length(var_name[i]) % spaces_in_tab;
  530.                 if (first_inc == spaces_in_tab)
  531.                     first_inc = 0;
  532.                 if (first_inc)
  533.                     {
  534.                     var_name[i] = var_name[i] "\t";
  535.                     diff -= first_inc;
  536.                     }
  537.                 while (diff > 0)
  538.                     {
  539.                     var_name[i] = var_name[i] "\t";
  540.                     diff -= spaces_in_tab;
  541.                     }
  542.                 }
  543.             # create dummy comment if none present
  544.             if (var_comment[i] == "")
  545.                 var_comment[i] = "// ";
  546.             # out go type, name, comment
  547.             out = out var_type[i] var_name[i] var_comment[i] "\r";
  548.             }
  549.         out = out "\t{\r";
  550.         }
  551.     }
  552.  
  553. # Output nicely formatted local definitions - note locals are optional
  554. function OutputLocals(    longest_type, longest_name, first_inc)
  555.     {
  556.     if (last_local <= last_param)
  557.         return;
  558.     #calculate longest type and param name
  559.     longest_type = 0;
  560.     longest_name = 0;
  561.     for (i = last_param+1; i <= last_local; ++i)
  562.         {
  563.         if (longest_type < length(var_type[i]))
  564.             longest_type = length(var_type[i])
  565.         if (longest_name < length(var_name[i]))
  566.             longest_name = length(var_name[i])
  567.         }
  568.     longest_name += 1; # 1 extra for ';'
  569.     # adjust up to next tab boundary
  570.     if (longest_type % spaces_in_tab)
  571.         longest_type += spaces_in_tab - longest_type % spaces_in_tab;
  572.     if (longest_name % spaces_in_tab)
  573.         longest_name += spaces_in_tab - longest_name % spaces_in_tab;
  574.     # and add a tab
  575.     longest_type += spaces_in_tab;
  576.     longest_name += spaces_in_tab;
  577.     # sneaky trick, attempt to squeeze short inits in with var_name
  578.     for (i = last_param+1; i <= last_local; ++i)
  579.         {
  580.         if (length(var_type[i]) + length(var_init[i]) + 1 <= longest_name)
  581.             {
  582.             var_name[i] = var_name[i] var_init[i];
  583.             delete var_init[i];
  584.             }
  585.         }
  586.     # now spit them out
  587.     for (i = last_param+1; i <= last_local; ++i)
  588.         {
  589.         # adjust type names
  590.         diff = longest_type - length(var_type[i])
  591.         first_inc = spaces_in_tab - length(var_type[i]) % spaces_in_tab;
  592.         var_type[i] = "\t" var_type[i];
  593.         if (first_inc == spaces_in_tab)
  594.             first_inc = 0;
  595.         if (first_inc)
  596.             {
  597.             var_type[i] = var_type[i] "\t";
  598.             diff -= first_inc;
  599.             }
  600.         while (diff > 0)
  601.             {
  602.             var_type[i] = var_type[i] "\t";
  603.             diff -= spaces_in_tab;
  604.             }
  605.         # adjust local names (always present), and append a semicolon, if no init
  606.         if (!(i in var_init) || var_init[i] == "")
  607.             {
  608.             var_name[i] = var_name[i] ";";
  609.             diff = longest_name - length(var_name[i])
  610.             first_inc = spaces_in_tab - length(var_name[i]) % spaces_in_tab;
  611.             if (first_inc == spaces_in_tab)
  612.                 first_inc = 0;
  613.             if (first_inc)
  614.                 {
  615.                 var_name[i] = var_name[i] "\t";
  616.                 diff -= first_inc;
  617.                 }
  618.             while (diff > 0)
  619.                 {
  620.                 var_name[i] = var_name[i] "\t";
  621.                 diff -= spaces_in_tab;
  622.                 }
  623.             }
  624.         else
  625.             var_name[i] = var_name[i] var_init[i] ";";
  626.         # create dummy comment if none present
  627.         if (var_comment[i] == "")
  628.             var_comment[i] = "// ";
  629.         # out go type, name, var_init, comment
  630.         #temp = var_type[i] var_name[i] var_init[i] var_comment[i];
  631.         # var_init has been thrown in with var_name just above
  632.         temp = var_type[i] var_name[i] var_comment[i];
  633.         out = out temp "\r";
  634.         }
  635.     }
  636.  
  637.  
  638. ## lexical functions ##
  639. function init()
  640.     {
  641.     NAME = 256
  642.     STRING = 257
  643.     CHAR_CONSTANT = 258
  644.     NUMBER = 259
  645.     KEY = 260
  646.     BITASSIGNOP = 261
  647.     ASSIGNOP = 262
  648.     LEX_OR = 263
  649.     LEX_AND = 264
  650.     RELOP = 265
  651.     SHIFT = 266
  652.     INCREMENT = 267
  653.     DECREMENT = 268
  654.     POINTER = 269
  655.     OTHER = 270
  656.     COMMENT = 271
  657.     EOF = 272
  658.     MEMBEROP = 273
  659.     ILLEGAL = 274
  660.     
  661.     }
  662.  
  663. # A simple, incomplete lexical analyzer for C - note it returns comments as tokens
  664. function advance()
  665.     {
  666.     if (clip == "" || error != 0)
  667.         {
  668.         toktype = EOF;
  669.         return;
  670.         }
  671.     if (toktype == EOF) return
  672.     if (tok == "." || tok == "->") #member coming, not a local
  673.         {
  674.         if (match(clip, /^[A-Za-z_](\w|_)*/))
  675.             {
  676.             tok = substr(clip, 1, RLENGTH)
  677.             clip = substr(clip, RLENGTH+1)
  678.             toktype = OTHER
  679.             return
  680.             }
  681.         else
  682.             require(0, "missing member name")
  683.         }
  684.     toktype = ILLEGAL
  685.     # grab comments, quotes, ticks, skip white
  686.     # if something was grabbed, including comment, return it now
  687.     if (skip_comments_etc())
  688.         return;
  689.     else if (clip == "" || error != 0)
  690.         {
  691.         toktype = EOF;
  692.         return;
  693.         }
  694.  
  695.     if (match(clip, /^[A-Za-z_](\w|_)*/) ||
  696.         match(clip, /^\.\.\./)) #name - note "..." treated as name.
  697.         {
  698.         tok = substr(clip, 1, RLENGTH)
  699.         clip = substr(clip, RLENGTH+1)
  700.         toktype = NAME
  701.         return
  702.         }
  703.     if (match(clip, /^([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?[fFlL]?/) ||
  704.         match(clip, /^0[0-7]+(u|U)?(l|L)?/) ||
  705.         match(clip, /^0(x|X)[0-9a-fA-F]+(u|U)?(l|L)?/))    #float or int
  706.         toktype = NUMBER;
  707.     else if (match(clip, /^(<<=|>>=|&=|\^=|\|=)/))        #bit assign
  708.         toktype = BITASSIGNOP
  709.     else if (match(clip, /^(\+=|-=|\*=|\/=|%=)/))        #assign, inc
  710.         toktype = ASSIGNOP
  711.     else if (match(clip, /^\|\|/))
  712.         toktype = LEX_OR
  713.     else if (match(clip, /^&&/))
  714.         toktype = LEX_AND
  715.     else if (match(clip, /^(<=|==|!=|>=)/))                #relational
  716.         toktype = RELOP
  717.     else if (match(clip, /^(<<|>>)/))                    #shift
  718.         toktype = SHIFT
  719.     else if (match(clip, /^\+\+/))
  720.         toktype = INCREMENT
  721.     else if (match(clip, /^--/))
  722.         toktype = DECREMENT
  723.     else if (match(clip, /^->/) || match(clip, /^->\*/))
  724.         toktype = POINTER
  725.     else if (match(clip, /^\./) || match(clip, /^\.\*/))
  726.         toktype = MEMBEROP
  727.     else if (match(clip, /^./))                            #everything else
  728.         {
  729.         #TO DO trap illegal tokens, eg "@"
  730.         toktype = substr(clip,1,1)
  731.         }
  732.     else
  733.         require(0, "Unexpected empty clip");
  734.     tok = substr(clip, 1, RLENGTH)
  735.     clip = substr(clip, RLENGTH+1)
  736.     if (error != 0)
  737.         toktype = EOF;
  738.     }
  739.  
  740. # grab comments, quotes, ticks, skip white
  741. function skip_comments_etc()
  742.     {
  743.     sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  744.     # is it a one-line comment?
  745.     if (match(clip, /^\/\/[^\r]*\r/))
  746.         {
  747.         tok = substr(clip, 1, RLENGTH-1)
  748.         clip = substr(clip, RLENGTH+1)
  749.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  750.         toktype = COMMENT
  751.         # nice touch, put a space at start of comment if missing
  752.         if (!match(tok, /^\/\/ /))
  753.             tok = "// " substr(tok, 3);
  754.         return 1;
  755.         }
  756.     # or a multi-line comment?
  757.     else if(match(clip, /^\/\*/))
  758.         {
  759.         GetComment();
  760.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  761.         toktype = COMMENT
  762.         return 1;
  763.         }
  764.     # or a string?
  765.     else if(match(clip, /^"/))
  766.         {
  767.         GetString();
  768.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  769.         toktype = STRING
  770.         return 1;
  771.         }
  772.     # or a tick thingy?
  773.     else if(match(clip, /^'/))
  774.         {
  775.         GetCharConstant();
  776.         sub(/^[ \t\r]+/, "", clip) #remove leading blanks and tabs
  777.         toktype = CHAR_CONSTANT
  778.         return 1;
  779.         }
  780.     # otherwise, return 0 meaning nothing significant captured
  781.     else
  782.         return 0;
  783.     }
  784.  
  785. # Extract /*...*/ style comment from clip
  786. function GetComment(        c, cp, i, len)
  787.     {
  788.     tok = "";
  789.     i = 4;
  790.     for (len = length(clip); i <= len; ++i)
  791.         {
  792.         c = substr(clip, i-1, 1);
  793.         cp = substr(clip, i, 1);
  794.         if (c == "*" && cp == "/")
  795.             {
  796.             tok = substr(clip, 1, i);
  797.             clip = substr(clip, i+1)
  798.             # Nice(?) touch, convert to inline if wanted
  799.             if (convert_comments)
  800.                 {
  801.                 sub(/^\/\*/, "//", tok);
  802.                 sub(/\*\/$/, "", tok);
  803.                 gsub(/\r[ \t]*/, "&// ", tok);
  804.                 # put a space at start of comment if missing
  805.                 if (!match(tok, /^\/\/ /))
  806.                     tok = "// " substr(tok, 3);
  807.                 }
  808.             return;
  809.             }
  810.         }
  811.     require(0, "unterminated comment")
  812.     }
  813.  
  814. # Get standard C string
  815. function GetString(        len, c, i, temp, esc)
  816.     {
  817.     
  818.     len = length(clip);
  819.     i = 2;
  820.     for (esc = 0; i <= len; ++i)
  821.         {
  822.         c = substr(clip, i, 1);
  823.         if (c == "\"")
  824.             {
  825.             if (esc == 0 || esc%2 == 0)
  826.                 {
  827.                 tok = substr(clip,1,i)
  828.                 clip = substr(clip, i+1)
  829.                 return;
  830.                 }
  831.             else
  832.                 esc = 0;
  833.             }
  834.         else if (c == "\\")
  835.             {
  836.             if (substr(clip, i+1, 1) == "\r") #end of line, string continued
  837.                 {
  838.                 esc = 0;
  839.                 }
  840.             else
  841.                 ++esc;
  842.             }
  843.         else if (c == "\r")
  844.             {
  845.             if (substr(clip, i-1, 1) == "\\") #string continued properly
  846.                 {
  847.                 ;
  848.                 }
  849.             else
  850.                 require(0, "unterminated string");
  851.             }
  852.         else
  853.             esc = 0;
  854.         }
  855.     require(0, "unterminated string");
  856.     }
  857.  
  858. # Get a thing in ticks''
  859. function GetCharConstant(        len, c, i, esc)
  860.     {
  861.     len = length(clip)
  862.     i = 2;
  863.     for (esc = 0; i <= len; ++i)
  864.         {
  865.         c = substr(clip, i, 1)
  866.         if (c == "'")
  867.             {
  868.             if (esc == 0 || esc%2 == 0)
  869.                 {
  870.                 tok = substr(clip,1,i)
  871.                 clip = substr(clip, i+1)
  872.                 return;
  873.                 }
  874.             else
  875.                 esc = 0
  876.             }
  877.         else if (c == "\\")
  878.             ++esc
  879.         else if (c == "\r")
  880.             require(0, "unterminated char constant")
  881.         else
  882.             esc = 0
  883.         }
  884.     require(0, "unterminated char constant")
  885.     }
  886.  
  887. # If assertion failed, stop work and print message to standard error
  888. # (Note contents of stderr are typically not flushed until you stop this program)
  889. function require(assertion, message)
  890.     {
  891.     if (assertion == 0)
  892.         {
  893.         error = 1;
  894.         print message >> "stderr";
  895.         toktype = EOF; # a bit of a hack, stop the lexer no matter what it takes
  896.         }
  897.     }
  898.  
  899.